<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片懒加载与缓存系统</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
        }

        .subtitle {
            font-size: 1.2rem;
            opacity: 0.9;
        }

        .dashboard {
            display: flex;
            gap: 20px;
            margin-bottom: 30px;
        }

        .cache-panel {
            flex: 1;
            background: rgba(0, 0, 0, 0.3);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
        }

        .view-controls {
            flex: 1;
            background: rgba(0, 0, 0, 0.3);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
        }

        .panel-title {
            font-size: 1.5rem;
            margin-bottom: 15px;
            color: #ffcc00;
            border-bottom: 2px solid #ffcc00;
            padding-bottom: 8px;
        }

        .stats {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
        }

        .stat-item {
            text-align: center;
            flex: 1;
        }

        .stat-value {
            font-size: 2rem;
            font-weight: bold;
            color: #4cd964;
        }

        .stat-label {
            font-size: 0.9rem;
            opacity: 0.8;
        }

        .progress-container {
            margin: 20px 0;
        }

        .progress-bar {
            height: 20px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 10px;
            overflow: hidden;
        }

        .progress {
            height: 100%;
            background: linear-gradient(90deg, #4cd964, #5ac8fa);
            border-radius: 10px;
            transition: width 0.5s ease;
            width: 0%;
        }

        .progress-text {
            text-align: center;
            margin-top: 5px;
            font-size: 0.9rem;
        }

        .cache-controls {
            display: flex;
            gap: 10px;
            margin-top: 20px;
        }

        button {
            padding: 10px 20px;
            border: none;
            border-radius: 8px;
            background: #5ac8fa;
            color: white;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            flex: 1;
        }

        button:hover {
            background: #4cd964;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }

        button:active {
            transform: translateY(0);
        }

        .view-options {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }

        .view-option {
            flex: 1;
            text-align: center;
            padding: 10px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .view-option.active {
            background: #5ac8fa;
            color: white;
        }

        .view-option:hover {
            background: rgba(255, 255, 255, 0.2);
        }

        .image-viewer {
            background: rgba(0, 0, 0, 0.3);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            margin-bottom: 30px;
        }

        .image-container {
            display: grid;
            grid-template-columns: repeat(1, 1fr);
            gap: 15px;
            margin-bottom: 20px;
            min-height: 400px;
        }

        .image-container.two-columns {
            grid-template-columns: repeat(2, 1fr);
        }

        .image-container.four-columns {
            grid-template-columns: repeat(2, 1fr);
        }

        @media (min-width: 768px) {
            .image-container.four-columns {
                grid-template-columns: repeat(4, 1fr);
            }
        }

        .image-item {
            position: relative;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
            background: rgba(255, 255, 255, 0.1);
            aspect-ratio: 4/3;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: transform 0.3s ease;
        }

        .image-item:hover {
            transform: scale(1.03);
        }

        .image-item img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
            transition: opacity 0.3s ease;
        }

        .image-placeholder {
            color: rgba(255, 255, 255, 0.5);
            font-size: 1.2rem;
        }

        .navigation {
            display: flex;
            justify-content: space-between;
            margin-top: 20px;
        }

        .nav-btn {
            padding: 12px 25px;
            font-size: 1.1rem;
        }

        .cache-status {
            margin-top: 20px;
            padding: 15px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
        }

        .cache-list {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 10px;
        }

        .cache-item {
            padding: 5px 10px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 5px;
            font-size: 0.8rem;
        }

        .cache-item.cached {
            background: rgba(76, 217, 100, 0.3);
        }

        .explanation {
            background: rgba(0, 0, 0, 0.3);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            margin-top: 30px;
        }

        .explanation h2 {
            color: #ffcc00;
            margin-bottom: 15px;
            border-bottom: 2px solid #ffcc00;
            padding-bottom: 8px;
        }

        .explanation ul {
            padding-left: 20px;
            margin-bottom: 15px;
        }

        .explanation li {
            margin-bottom: 10px;
            line-height: 1.5;
        }

        .highlight {
            color: #4cd964;
            font-weight: bold;
        }

        .loading {
            opacity: 0.3;
        }

        .loaded {
            opacity: 1;
        }
    </style>
</head>

<body>
    <div class="container">
        <header>
            <h1>图片懒加载与缓存系统</h1>
            <p class="subtitle">动态加载技术，让图片浏览如丝般顺滑</p>
        </header>

        <div class="dashboard">
            <div class="cache-panel">
                <h2 class="panel-title">缓存控制面板</h2>
                <div class="stats">
                    <div class="stat-item">
                        <div class="stat-value" id="total-images">0</div>
                        <div class="stat-label">总图片数量</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-value" id="cached-images">0</div>
                        <div class="stat-label">已缓存图片</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-value" id="cache-batch">10</div>
                        <div class="stat-label">每批缓存数量</div>
                    </div>
                </div>

                <div class="progress-container">
                    <div class="progress-bar">
                        <div class="progress" id="cache-progress"></div>
                    </div>
                    <div class="progress-text" id="progress-text">0%</div>
                </div>

                <div class="cache-controls">
                    <button id="start-cache">开始缓存</button>
                    <button id="pause-cache">暂停缓存</button>
                    <button id="reset-cache">重置缓存</button>
                </div>

                <div class="cache-status">
                    <h3>缓存状态</h3>
                    <div class="cache-list" id="cache-list"></div>
                </div>
            </div>

            <div class="view-controls">
                <h2 class="panel-title">浏览控制</h2>
                <div class="stats">
                    <div class="stat-item">
                        <div class="stat-value" id="current-index">1</div>
                        <div class="stat-label">当前图片</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-value" id="view-mode">1</div>
                        <div class="stat-label">查看模式</div>
                    </div>
                    <div class="stat-item">
                        <div class="stat-value" id="load-speed">0ms</div>
                        <div class="stat-label">加载速度</div>
                    </div>
                </div>

                <h3>查看方式</h3>
                <div class="view-options">
                    <div class="view-option active" data-mode="1">单张查看</div>
                    <div class="view-option" data-mode="2">两张查看</div>
                    <div class="view-option" data-mode="4">四张查看</div>
                </div>

                <div class="navigation">
                    <button class="nav-btn" id="prev-btn">上一张</button>
                    <button class="nav-btn" id="next-btn">下一张</button>
                </div>
            </div>
        </div>

        <div class="image-viewer">
            <h2 class="panel-title">图片浏览区</h2>
            <div class="image-container" id="image-container">
                <!-- 图片将通过JavaScript动态加载 -->
                <div class="image-item">
                    <div class="image-placeholder">图片将在这里显示</div>
                </div>
            </div>
        </div>

        <div class="explanation">
            <h2>实现原理总结</h2>
            <ul>
                <li><span class="highlight">分批次缓存</span>：将大量图片分成小批次（每次10张）进行缓存，避免一次性加载造成卡顿。</li>
                <li><span class="highlight">缓存优先</span>：浏览图片时优先从缓存中读取，缓存中没有的图片才从服务器请求。</li>
                <li><span class="highlight">预加载机制</span>：在浏览当前图片时，预加载后续几张图片，实现无缝切换。</li>
                <li><span class="highlight">可视化进度</span>：通过进度条和统计数据直观展示缓存过程，提升用户体验。</li>
                <li><span class="highlight">响应式布局</span>：支持单张、两张、四张等多种查看模式，适应不同浏览需求。</li>
                <li><span class="highlight">性能优化</span>：使用异步加载、图片压缩、缓存策略等技术确保流畅体验。</li>
            </ul>
            <p>通过以上技术组合，系统能够处理大量图片数据，同时保持流畅的用户体验，让用户几乎感觉不到加载过程。</p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            // 模拟图片数据 - 实际应用中这些URL应该来自服务器
            const imageUrls = [
                'https://picsum.photos/400/300?random=1',
                'https://picsum.photos/400/300?random=2',
                'https://picsum.photos/400/300?random=3',
                'https://picsum.photos/400/300?random=4',
                'https://picsum.photos/400/300?random=5',
                'https://picsum.photos/400/300?random=6',
                'https://picsum.photos/400/300?random=7',
                'https://picsum.photos/400/300?random=8',
                'https://picsum.photos/400/300?random=9',
                'https://picsum.photos/400/300?random=10',
                'https://picsum.photos/400/300?random=11',
                'https://picsum.photos/400/300?random=12',
                'https://picsum.photos/400/300?random=13',
                'https://picsum.photos/400/300?random=14',
                'https://picsum.photos/400/300?random=15',
                'https://picsum.photos/400/300?random=16',
                'https://picsum.photos/400/300?random=17',
                'https://picsum.photos/400/300?random=18',
                'https://picsum.photos/400/300?random=19',
                'https://picsum.photos/400/300?random=20',
                'https://picsum.photos/400/300?random=21',
                'https://picsum.photos/400/300?random=22',
                'https://picsum.photos/400/300?random=23',
                'https://picsum.photos/400/300?random=24',
                'https://picsum.photos/400/300?random=25',
                'https://picsum.photos/400/300?random=26',
                'https://picsum.photos/400/300?random=27',
                'https://picsum.photos/400/300?random=28',
                'https://picsum.photos/400/300?random=29',
                'https://picsum.photos/400/300?random=30'
            ];

            // 状态变量
            const state = {
                totalImages: imageUrls.length,
                cachedImages: 0,
                currentIndex: 0,
                viewMode: 1, // 1, 2, 4
                isCaching: false,
                cacheBatchSize: 10,
                imageCache: new Map(), // 使用Map存储已缓存的图片
                cacheQueue: [...imageUrls], // 待缓存的图片队列
                startTime: null,
                imageElements: new Map() // 存储已创建的图片DOM元素
            };

            // DOM元素
            const elements = {
                totalImages: document.getElementById('total-images'),
                cachedImages: document.getElementById('cached-images'),
                cacheBatch: document.getElementById('cache-batch'),
                cacheProgress: document.getElementById('cache-progress'),
                progressText: document.getElementById('progress-text'),
                startCache: document.getElementById('start-cache'),
                pauseCache: document.getElementById('pause-cache'),
                resetCache: document.getElementById('reset-cache'),
                cacheList: document.getElementById('cache-list'),
                currentIndex: document.getElementById('current-index'),
                viewMode: document.getElementById('view-mode'),
                loadSpeed: document.getElementById('load-speed'),
                imageContainer: document.getElementById('image-container'),
                prevBtn: document.getElementById('prev-btn'),
                nextBtn: document.getElementById('next-btn'),
                viewOptions: document.querySelectorAll('.view-option')
            };

            // 初始化UI
            function initUI() {
                elements.totalImages.textContent = state.totalImages;
                elements.cachedImages.textContent = state.cachedImages;
                elements.cacheBatch.textContent = state.cacheBatchSize;
                updateProgress();
                updateCacheList();
                updateImageViewer();

                // 添加事件监听器
                elements.startCache.addEventListener('click', startCaching);
                elements.pauseCache.addEventListener('click', pauseCaching);
                elements.resetCache.addEventListener('click', resetCaching);
                elements.prevBtn.addEventListener('click', showPrevImages);
                elements.nextBtn.addEventListener('click', showNextImages);

                elements.viewOptions.forEach(option => {
                    option.addEventListener('click', function () {
                        // 更新活跃状态
                        elements.viewOptions.forEach(opt => opt.classList.remove('active'));
                        this.classList.add('active');

                        // 更新查看模式
                        state.viewMode = parseInt(this.dataset.mode);
                        elements.viewMode.textContent = state.viewMode;

                        // 更新图片显示
                        updateImageViewer();
                    });
                });
            }

            // 更新进度条
            function updateProgress() {
                const progress = (state.cachedImages / state.totalImages) * 100;
                elements.cacheProgress.style.width = `${progress}%`;
                elements.progressText.textContent = `${progress.toFixed(1)}%`;
            }

            // 更新缓存列表
            function updateCacheList() {
                elements.cacheList.innerHTML = '';

                for (let i = 0; i < state.totalImages; i++) {
                    const cacheItem = document.createElement('div');
                    cacheItem.className = `cache-item ${i < state.cachedImages ? 'cached' : ''}`;
                    cacheItem.textContent = `图片 ${i + 1}`;
                    elements.cacheList.appendChild(cacheItem);
                }
            }

            // 开始缓存
            function startCaching() {
                if (state.isCaching || state.cacheQueue.length === 0) return;

                state.isCaching = true;
                state.startTime = Date.now();
                cacheNextBatch();
            }

            // 暂停缓存
            function pauseCaching() {
                state.isCaching = false;
            }

            // 重置缓存
            function resetCaching() {
                state.isCaching = false;
                state.cachedImages = 0;
                state.cacheQueue = [...imageUrls];
                state.imageCache.clear();
                state.imageElements.clear();

                elements.cachedImages.textContent = state.cachedImages;
                updateProgress();
                updateCacheList();
                updateImageViewer();
            }

            // 缓存下一批图片
            function cacheNextBatch() {
                if (!state.isCaching || state.cacheQueue.length === 0) return;

                const batch = state.cacheQueue.splice(0, state.cacheBatchSize);
                let completed = 0;

                batch.forEach((url, index) => {
                    const imgIndex = imageUrls.indexOf(url);

                    // 如果图片已经缓存，跳过
                    if (state.imageCache.has(url)) {
                        completed++;
                        if (completed === batch.length && state.cacheQueue.length > 0) {
                            setTimeout(cacheNextBatch, 100);
                        }
                        return;
                    }

                    const img = new Image();
                    img.onload = function () {
                        // 图片加载完成，存入缓存
                        state.imageCache.set(url, img);
                        state.cachedImages++;
                        elements.cachedImages.textContent = state.cachedImages;

                        completed++;

                        // 更新进度
                        updateProgress();
                        updateCacheList();

                        // 如果当前显示的图片在缓存中，更新显示
                        if (state.currentIndex <= imgIndex && imgIndex < state.currentIndex + state.viewMode) {
                            updateImageDisplay(imgIndex);
                        }

                        // 如果这批全部完成，继续下一批
                        if (completed === batch.length) {
                            // 计算平均加载速度
                            const elapsed = Date.now() - state.startTime;
                            const avgSpeed = elapsed / state.cachedImages;
                            elements.loadSpeed.textContent = `${avgSpeed.toFixed(0)}ms`;

                            if (state.cacheQueue.length > 0) {
                                setTimeout(cacheNextBatch, 100);
                            } else {
                                state.isCaching = false;
                            }
                        }
                    };

                    img.onerror = function () {
                        // 处理加载失败的情况
                        console.error(`Failed to load image: ${url}`);
                        completed++;

                        if (completed === batch.length && state.cacheQueue.length > 0) {
                            setTimeout(cacheNextBatch, 100);
                        }
                    };

                    img.src = url;
                });
            }

            // 更新单个图片的显示
            function updateImageDisplay(index) {
                const imageItem = state.imageElements.get(index);
                if (!imageItem) return;

                const url = imageUrls[index];
                const cachedImg = state.imageCache.get(url);

                if (cachedImg) {
                    // 如果已经有img元素，直接更新src（避免重新创建）
                    const existingImg = imageItem.querySelector('img');
                    if (existingImg) {
                        if (existingImg.src !== cachedImg.src) {
                            existingImg.src = cachedImg.src;
                            existingImg.classList.remove('loading');
                            existingImg.classList.add('loaded');
                        }
                    } else {
                        // 如果没有img元素，创建新的
                        const img = cachedImg.cloneNode();
                        img.classList.add('loaded');
                        imageItem.innerHTML = '';
                        imageItem.appendChild(img);
                    }
                }
            }

            // 更新图片显示
            function updateImageViewer() {
                elements.imageContainer.innerHTML = '';
                elements.imageContainer.className = 'image-container';

                // 根据查看模式设置CSS类
                if (state.viewMode === 2) {
                    elements.imageContainer.classList.add('two-columns');
                } else if (state.viewMode === 4) {
                    elements.imageContainer.classList.add('four-columns');
                }

                // 计算当前显示的图片范围
                const startIndex = state.currentIndex;
                const endIndex = Math.min(startIndex + state.viewMode, state.totalImages);

                // 更新当前索引显示
                elements.currentIndex.textContent = `${startIndex + 1}-${endIndex}`;

                // 创建图片元素
                for (let i = startIndex; i < endIndex; i++) {
                    let imageItem = state.imageElements.get(i);

                    if (!imageItem) {
                        // 创建新的图片容器
                        imageItem = document.createElement('div');
                        imageItem.className = 'image-item';
                        state.imageElements.set(i, imageItem);
                    } else {
                        // 清空现有内容但保留容器
                        imageItem.innerHTML = '';
                    }

                    const url = imageUrls[i];
                    const cachedImg = state.imageCache.get(url);

                    if (cachedImg) {
                        // 从缓存中获取图片
                        const img = cachedImg.cloneNode();
                        img.classList.add('loaded');
                        imageItem.appendChild(img);
                    } else {
                        // 显示占位符
                        const placeholder = document.createElement('div');
                        placeholder.className = 'image-placeholder';
                        placeholder.textContent = `图片 ${i + 1} 加载中...`;
                        imageItem.appendChild(placeholder);

                        // 如果正在缓存，可以尝试预加载
                        if (!state.isCaching) {
                            preloadImage(i);
                        }
                    }

                    elements.imageContainer.appendChild(imageItem);
                }
            }

            // 预加载单个图片
            function preloadImage(index) {
                const url = imageUrls[index];
                if (state.imageCache.has(url)) return;

                const img = new Image();
                img.onload = function () {
                    state.imageCache.set(url, img);
                    state.cachedImages++;
                    elements.cachedImages.textContent = state.cachedImages;
                    updateProgress();
                    updateCacheList();
                    updateImageDisplay(index);
                };
                img.src = url;
            }

            // 显示上一组图片
            function showPrevImages() {
                if (state.currentIndex - state.viewMode >= 0) {
                    state.currentIndex -= state.viewMode;
                    updateImageViewer();
                }
            }

            // 显示下一组图片
            function showNextImages() {
                if (state.currentIndex + state.viewMode < state.totalImages) {
                    state.currentIndex += state.viewMode;
                    updateImageViewer();

                    // 预加载下一批图片
                    preloadNextBatch();
                }
            }

            // 预加载下一批图片
            function preloadNextBatch() {
                const nextStartIndex = state.currentIndex + state.viewMode;
                const preloadEndIndex = Math.min(nextStartIndex + state.cacheBatchSize, state.totalImages);

                for (let i = nextStartIndex; i < preloadEndIndex; i++) {
                    if (!state.imageCache.has(imageUrls[i])) {
                        preloadImage(i);
                    }
                }
            }

            // 初始化
            initUI();
        });
    </script>
</body>

</html>